package com.flex.showdoc4j.executor;

import com.flex.showdoc4j.config.Configuration;
import com.flex.showdoc4j.executor.interfaces.ClientExecutor;
import com.flex.showdoc4j.executor.interfaces.ParameterResolver;
import com.flex.showdoc4j.model.DocModel;
import com.flex.showdoc4j.reflection.FieldReflection;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.util.ObjectUtils;
import org.springframework.web.method.HandlerMethod;

import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
import java.util.*;

/**
 * 执行本地解析
 *
 * @author ganlt<br>
 * @since 2019/06/26 18:23 <br>
 */
@Slf4j
public class JvmClientExecutor implements ClientExecutor {

    @Override
    public String executor(Wrapper wrapper) {
        Configuration configuration = wrapper.getConfiguration();
        Object controlBean = configuration.getControlBean(wrapper.getPatternUrl());
        ApplicationContext applicationContext = configuration.getApplicationContext();
        if (controlBean instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) controlBean;
            Method method = handlerMethod.getMethod();
            Object bean = handlerMethod.getBean();
            String methodName = method.getName();
            DocModel docModel = wrapper.getDocModel();
            String beanName;
            if (bean instanceof String) {
                beanName = (String) bean;
            } else {
                beanName = bean.getClass().getSimpleName();
            }
            try {
                //构造参数
                ParameterResolver parameterResolver = configuration.getParameterResolver();
                List<?> realityObjList = parameterResolver.resolver(wrapper);
                List<Class<?>> signalParameters = typeConvertClass(parameterResolver.signalParameters(wrapper));
                Object beanObject = applicationContext.getBean(beanName);
                Object result = beanObject.getClass().getMethod(methodName, signalParameters.toArray(new Class[signalParameters.size()]))
                        .invoke(beanObject, realityObjList.toArray(new Object[realityObjList.size()]));
                String returnResult = JSON.toJSONString(result, SerializerFeature.WriteEnumUsingToString, SerializerFeature.PrettyFormat);
                wrapper.setRetrunJsonStr(returnResult);
                new ReturnVariable(wrapper).wrapperReturnVariable(result);
                Object requestRealityParam = listToMap(docModel.isPage(), docModel.getPager(), realityObjList);
                wrapper.setRequestParamJsonStr(JSON.toJSONString(requestRealityParam, SerializerFeature.PrettyFormat,SerializerFeature.WriteEnumUsingToString));

                return returnResult;
            } catch (Exception e) {
                log.error("解析地址:{},失败,原因:{}", wrapper.getPatternUrl(), e);
            }
        }
        return null;
    }

    private List<Class<?>> typeConvertClass(List<Type> typeList) {
        List<Class<?>> list = new ArrayList<>();
        try {
            for (Type type : typeList) {
                if (type instanceof ParameterizedType) {
                    list.add((Class<?>) ((ParameterizedType) type).getRawType());
                } else {
                    list.add((Class<?>) type);
                }
            }
        } catch (Exception e) {
        }
        return list;
    }


    /**
     * list转为map
     *
     * @param isPager 是否分页
     * @param pages   分页值
     * @param list    实际参数值列表
     * @return map参数对象
     */
    private Object listToMap(boolean isPager, int[] pages, List<?> list) {
        Map<String, Object> tmpMap = new HashMap<>(6);
        for (Object obj : list) {
            if (obj instanceof String) {
                log.info("暂未实现String参数映射");
            } else if (obj instanceof Number) {
                log.info("暂未实现Number参数映射");
            } else if (obj instanceof Map) {
                log.info("暂未实现Map参数映射");
            } else if (obj instanceof Collection) {
                return JSON.toJSONString(obj, SerializerFeature.WriteEnumUsingToString);
            } else {
                PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(obj.getClass());
                for (PropertyDescriptor descriptor : propertyDescriptors) {
                    Object value = getProperty(descriptor, obj);
                    String name = descriptor.getName();
                    if (!ObjectUtils.isEmpty(value) && !"class".equals(name)) {
                        tmpMap.put(name, value);
                    }
                }
            }

        }
        return processPager(isPager, pages, tmpMap);
    }

    private Object getProperty(PropertyDescriptor dp, Object obj) {
        //获取对象属性方法
        Method method = dp.getReadMethod();
        //获取属性对应的值
        Object retValue = null;
        try {
            if (method != null) {
                retValue = method.invoke(obj);
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return retValue;
    }

    private Map<String, Object> processPager(boolean isPager, int[] pages, Map<String, Object> paramMap) {
        if (!isPager) {
            return paramMap;
        }

        Map<String, Object> pageParamMap = new HashMap<>(5);
        Map<String, Object> pagerMap = new HashMap<>(5);
        pagerMap.put("currentPage", pages[0]);
        pagerMap.put("pageSize", pages[1]);

        Map<String, Object> paramMaps = new HashMap<>(8);
        Object contentParam = pageExtraObject(paramMap);
        if (contentParam == null) {
            return paramMap;
        }
        if (contentParam instanceof JSONObject) {
            JSONObject jsonObject = (JSONObject) contentParam;
            jsonObject.forEach(paramMaps::put);
        } else {
            List<Field> fields = FieldReflection.getFields(contentParam, false);
            fields.forEach(field -> {
                String name = field.getName();
                Object value = FieldReflection.getValue(field, contentParam);
                paramMaps.put(name, value);
            });
        }
        pageParamMap.put("content", paramMaps);
        pageParamMap.put("pager", pagerMap);

        return pageParamMap;
    }

    /**
     * 参数提取实际对象
     *
     * @param result 解析参数
     * @return 返回分页实际存储对象
     */
    private Object pageExtraObject(Object result) {
        if (result == null) {
            return null;
        }
        return FieldReflection.getDescriptor(result, Wrapper.RPARAM_ATTRIBUTE_NAME);
    }


}
